/*
 *  Filename: lsv_gc_bitmap.c
 *  Description:
 *
 *  Created on: 2017年3月27日
 *  Author: gj (825674301)
 */
#include "lsv_conf.h"
#include "lsv_help.h"
#include "lsv_log.h"
#include "lsv_volume.h"
#include "lsv_gc.h"


int lsv_gc_bitmap_set_head(lsv_volume_proto_t *lsv_info) {
        int ret = 0;
        lsv_log_info_t *log_info = NULL;
        lsv_gc_context_t *gc_context = NULL;
        int8_t buff[LSV_PAGE_SIZE];
        lsv_volume_io_t vio;
        uint32_t len;

        log_info = lsv_info->log_info;
        gc_context = log_info->gc_context;

        memset(buff, 0, LSV_PAGE_SIZE);
        lsv_gc_old_storage_bitmap_meta_t *meta = (lsv_gc_old_storage_bitmap_meta_t *)buff;

        for (int i = 0; i < LSV_GC_BITMAP_CHUNK_NUM; i++) {
                if (NULL == gc_context->bitmaps[i]) {
                        meta->chunk_ids[i] = 0;
                } else {
                        meta->chunk_ids[i] = gc_context->bitmaps[i]->chunk_id;
                }
                DINFO("%u/%lu chunk_id %u\n", i, LSV_GC_BITMAP_CHUNK_NUM, meta->chunk_ids[i]);
        }

        len = sizeof(*meta);
        YASSERT(len <= LSV_PAGE_SIZE / 2);

        //write info
        lsv_volume_io_init(&vio,
                           LSV_PRIM_CHUNK_ID,
                           lsv_info->u.gc_os_page_id * LSV_PAGE_SIZE + LSV_GC_BITMAP_HEAD,
                           len,
                           LSV_GC_STORAGE_TYPE);
        ret = lsv_volume_chunk_update(lsv_info, &vio, buff);
        if (ret < 0) {
                DERROR("set storage_head stroage err,errno:%d\n", ret);
                YASSERT(0);
        }

        DINFO("set storage_head\n");
        return 0;
}

static int __lsv_gc_record_create(lsv_volume_proto_t *lsv_info, lsv_u32_t storage_chkid) {
        int ret = 0;
        lsv_log_info_t *log_info = NULL;
        lsv_gc_context_t *gc_context = NULL;
        lsv_gc_bitmap_record_t *bitmap_record = NULL;
        lsv_volume_io_t vio;

        log_info = lsv_info->log_info;
        gc_context = log_info->gc_context;

        bitmap_record = gc_context->bitmaps[storage_chkid];
        if (NULL == bitmap_record) {
                bitmap_record = (lsv_gc_bitmap_record_t *) malloc(sizeof(lsv_gc_bitmap_record_t));
                if (NULL == bitmap_record) {
                        ret = -ENOMEM;
                        DERROR("malloc bitmap_record failed,errno:%d\n", ret);
                        goto ERR1;
                }

                ret = lsv_volume_chunk_malloc(lsv_info, LSV_GC_STORAGE_TYPE,
                                             &bitmap_record->chunk_id);
                if (ret < 0) {
                        DERROR("malloc bitmap_record failed,errno:%d\n", ret);
                        goto ERR2;
                }

                memset(bitmap_record->bitmap, 0x00, LSV_CHUNK_SIZE);
                lsv_volume_io_init(&vio, bitmap_record->chunk_id, 0, LSV_CHUNK_SIZE,
                                   LSV_GC_STORAGE_TYPE);
                ret = lsv_volume_chunk_update(lsv_info, &vio, (lsv_s8_t *)bitmap_record->bitmap);
                if (ret < 0) {
                        DERROR("set storage bitmap err,errno:%d\n", ret);
                        YASSERT(0);
                }

                gc_context->bitmaps[storage_chkid] = bitmap_record;

                lsv_gc_bitmap_set_head(lsv_info);
        }

        return 0;
ERR2:
        if (bitmap_record) {
                free(bitmap_record);
        }
ERR1:
        return ret;
}

static int __lsv_gc_bitmap_set(lsv_volume_proto_t *lsv_info, lsv_u32_t chunk_id) {

        int ret = 0;
        lsv_log_info_t *log_info = NULL;
        lsv_gc_context_t *gc_context = NULL;
        lsv_u32_t storage_chkid, storage_offset;
        lsv_gc_bitmap_record_t *bitmap_record = NULL;
        lsv_volume_io_t vio;

        log_info = lsv_info->log_info;
        gc_context = log_info->gc_context;

        storage_chkid = chunk_id / LSV_GC_STORAGE_CHUNK_SIZE;

        // 对齐到页上
        storage_offset = (chunk_id % LSV_GC_STORAGE_CHUNK_SIZE) * LSV_GC_STORAGE_BIT_LEN / 8 / LSV_PAGE_SIZE
                        * LSV_PAGE_SIZE;

        bitmap_record = gc_context->bitmaps[storage_chkid];
        YASSERT(NULL!=bitmap_record);
        DINFO("loggc set bitmaps chunk_id:%u,<%u,%u,%u>\n", chunk_id, storage_chkid, bitmap_record->chunk_id,
              storage_offset);

        //set
        lsv_volume_io_init(&vio, bitmap_record->chunk_id, storage_offset, LSV_PAGE_SIZE, LSV_GC_STORAGE_TYPE);
        ret = lsv_volume_chunk_update(lsv_info, &vio, (lsv_s8_t *)&bitmap_record->bitmap[storage_offset]);
        if (ret < 0) {
                DERROR("loggc set bitmaps stroage err,errno:%d\n", ret);
                YASSERT(0);
        }

        return 0;
}

/**
 *
 * @param lsv_info
 * @param chunk_id
 * @param is_use 1 | 0
 * @return
 */
int lsv_gc_bitmap_use(lsv_volume_proto_t *lsv_info, lsv_u32_t chunk_id, int is_use) {
        int ret = 0;
        lsv_log_info_t *log_info = NULL;
        lsv_gc_context_t *gc_context = NULL;
        lsv_u32_t storage_chkid;
        lsv_gc_bitmap_record_t *bitmap_record = NULL;
        lsv_u32_t bitmap_pos;
        lsv_u8_t bitmap_mask;

        log_info = (lsv_log_info_t*) lsv_info->log_info;
        gc_context = log_info->gc_context;

        YASSERT(chunk_id < LSV_MAX_CHUNK_NUM);
        YASSERT(is_use == 0 || is_use == 1);

        // 第几个chunk？
        storage_chkid = chunk_id / LSV_GC_STORAGE_CHUNK_SIZE;

        lsv_lock(gc_context->bitmap_lock + storage_chkid);

        bitmap_record = gc_context->bitmaps[storage_chkid];
        if (bitmap_record == NULL && is_use) {
                ret = __lsv_gc_record_create(lsv_info, storage_chkid);
                if (ret < 0) {
                        DERROR("storage_record_create err,errno:%d\n", ret);
                        GOTO(ERR, ret);
                }
                bitmap_record = gc_context->bitmaps[storage_chkid];
        }
        YASSERT(NULL != bitmap_record);

        // chunk内第几个byte？
        bitmap_pos = (chunk_id % LSV_GC_STORAGE_CHUNK_SIZE) / (8 / LSV_GC_STORAGE_BIT_LEN);

        // 字符内偏移，及其对应位设定为1
        bitmap_mask = ((lsv_u8_t) 0x01) << (chunk_id % (8 / LSV_GC_STORAGE_BIT_LEN) * LSV_GC_STORAGE_BIT_LEN);

        if (is_use) {
                bitmap_record->bitmap[bitmap_pos] |= bitmap_mask;
        } else {
                bitmap_record->bitmap[bitmap_pos] &= ~bitmap_mask;
        }

        DINFO("loggc use bitmap chunk_id:%u,<%u,%u>,%u\n", chunk_id, bitmap_pos, bitmap_mask,
              bitmap_record->bitmap[bitmap_pos]);

        __lsv_gc_bitmap_set(lsv_info, chunk_id);

        lsv_unlock(gc_context->bitmap_lock + storage_chkid);

        return 0;
ERR:
        return ret;
}

#if 0
static int __lsv_gc_bitmap_unuse(lsv_volume_proto_t *lsv_info, lsv_u32_t chunk_id) {
        lsv_log_info_t *log_info = NULL;
        lsv_gc_context_t *gc_context = NULL;
        lsv_u32_t storage_chkid;
        lsv_gc_bitmap_record_t *bitmap_record = NULL;
        lsv_u32_t bitmap_pos;
        lsv_u8_t bitmap_mask;

        YASSERT(chunk_id < LSV_MAX_CHUNK_NUM);

        log_info = lsv_info->log_info;
        gc_context = log_info->gc_context;

        storage_chkid = chunk_id / LSV_GC_STORAGE_CHUNK_SIZE;

        lsv_lock(gc_context->bitmap_lock + storage_chkid);
        bitmap_record = gc_context->bitmaps[storage_chkid];

        YASSERT(NULL != bitmap_record);

        bitmap_pos = (chunk_id % LSV_GC_STORAGE_CHUNK_SIZE) / (8 / LSV_GC_STORAGE_BIT_LEN);
        bitmap_mask = ~(((lsv_u8_t) 0x01) << (chunk_id % (8 / LSV_GC_STORAGE_BIT_LEN)) * LSV_GC_STORAGE_BIT_LEN);
        bitmap_record->bitmap[bitmap_pos] &= bitmap_mask;

        DINFO("loggc unuse bitmap chunk_id:%u,<%u,%u>,%u\n", chunk_id, bitmap_pos, bitmap_mask,
              bitmap_record->bitmap[bitmap_pos]);

        __lsv_gc_bitmap_set(lsv_info, chunk_id);

        lsv_unlock(gc_context->bitmap_lock + storage_chkid);
        return 0;
}
#endif
